# -*- coding: utf-8 -*-
# @Author: bao
# @Date:   2017-01-23 18:50:01
# @Last Modified by:   bao
# @Last Modified time: 2017-03-07 21:35:02
#!/usr/bin/env python

from datetime import datetime
import sys
import copy
import math
import random
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
import demand_estimation as dm_est

from gen_topo_fully import *
from gen_topo_detour import *
from gen_topo_ovs import *

from flow_gen import *
from topo_gen import *

if __name__ == '__main__':
    
    random.seed(3)
    # sys paramters
    switch_nums = 64
    host_num = 10
    host_nums = switch_nums*host_num
    degree = 10
    row_size = int(math.ceil(math.sqrt(switch_nums)))
    threshold = 0.4
    demand_type = 'random' #cluster
    cluster_size = 5

    for i in range(1, len(sys.argv)):
        if sys.argv[i] == 'switch_nums':
            switch_nums = int(sys.argv[i+1])
        if sys.argv[i] == 'host_num':
            host_num = int(sys.argv[i+1])    
        if sys.argv[i] == 'degree':
            degree = int(sys.argv[i+1])
        if sys.argv[i] == 'demand_type':
            demand_type = sys.argv[i+1]
        if sys.argv[i] == 'row_size':
            row_size = sys.argv[i+1]
    
    # initial
    switchList = {}
    host_location = {} #{host_ip:(dpid,port)}

    for switch in xrange(1,switch_nums+1):
        hosts = []
        for host in xrange(0,host_num):
            hosts.append(host+(switch-1)*host_num)
            host_location[host+(switch-1)*host_num]=(switch, degree+host+1)
        switchList[switch] = hosts
    
    # initial bipartite graph
    tf_graph = nx.Graph()
    wl_graph = nx.Graph()

    bp_src = range (1,switch_nums+1)
    bp_dst = range (switch_nums+1, switch_nums*2+1)

    for src in bp_src:
        tf_graph.add_node(src, bipartite=0, degree=0)
        wl_graph.add_node(src, bipartite=0, degree=0)

    for dst in bp_dst:
        tf_graph.add_node(dst, bipartite=1, degree=0)
        wl_graph.add_node(dst, bipartite=1, degree=0)

    # calculate initial matchings
    print "k_regular_network"
    pre_matches = k_regular_network(switch_nums, degree)

    # 0.Make flows
    flows_src = {}
    flows_dst = {}
    makeFlows(flows_src, flows_dst, demand_type, switch_nums, host_num, host_nums, cluster_size, threshold)
  
    # 1.Max-min fariness
    Host_Matrix = dm_est.demand_estimation(flows_src, flows_dst)

    # 2.Switch-level traffic demand
    dm_est.transform(Host_Matrix, host_location, tf_graph, switch_nums, threshold)

    # Fully reconfigurable Non-blocking
    if False:
        # 3.generate topology and new matchings
        gen_topo_fully = gen_topo_fully()
        gen_topo_fully._transform(wl_graph, tf_graph, degree, switch_nums)
        cur_matches_fully = gen_topo_fully._matchings(wl_graph, pre_matches, degree, switch_nums)
        (d_matches, s_matches, new_matches) = gen_topo_fully._color_assignment(pre_matches, cur_matches_fully, switch_nums)
        print len(d_matches), len(s_matches), len(new_matches)

        if True:
            for k in new_matches:
                print 'new_matches',k, len(new_matches[k])
                print sorted(new_matches[k])
            
            for k in s_matches:
                print 's_matches',k, len(s_matches[k])
                print sorted(s_matches[k])
            d_sum = 0
            for k in d_matches:
                d_sum += len(d_matches[k])
                print 'd_matches',k, len(d_matches[k])
                print sorted(d_matches[k])
            print d_sum
    
    # Detour reconfigurable Non-blocking
    if True:
        # 3.generate topology and new matchings
        gen_topo_detour = gen_topo_detour()
        s_matches = {}
        d_matches = {}
        for x in pre_matches:
            s_matches[x] = copy.copy(pre_matches[x])
            d_matches[x] = []
        gen_topo_detour._wavelength_adjustment(pre_matches, d_matches, s_matches, row_size, switch_nums)
        gen_topo_detour._transform(wl_graph, tf_graph, degree, switch_nums)
        cur_matches_detour = gen_topo_detour._matchings(wl_graph, pre_matches, degree, switch_nums)
        (d_matches, s_matches, new_matches) = gen_topo_detour._color_assignment(pre_matches, cur_matches_detour, switch_nums)
        d_sum = 0
        for k in d_matches:
            d_sum += len(d_matches[k])
            print 'd_matches',k, len(d_matches[k])
            print sorted(d_matches[k])
        print d_sum
        gen_topo_detour._wavelength_adjustment(new_matches, d_matches, s_matches, row_size, switch_nums)
        print len(d_matches), len(s_matches), len(new_matches)

        if True:
            for k in new_matches:
                print 'new_matches',k, len(new_matches[k])
                print sorted(new_matches[k])
            
            for k in s_matches:
                print 's_matches',k, len(s_matches[k])
                print sorted(s_matches[k])
            d_sum = 0
            for k in d_matches:
                d_sum += len(d_matches[k])
                print 'd_matches',k, len(d_matches[k])
                print sorted(d_matches[k])
            print d_sum
    
    # OvS reconfigurable with blocking
    if False:
        # 3.generate topology and new matchings
        gen_topo_ovs = gen_topo_ovs()
        gen_topo_ovs._transform(wl_graph, tf_graph, degree, row_size, switch_nums)
        cur_matches_fully = gen_topo_ovs._matchings(wl_graph, pre_matches, degree, switch_nums)
        (d_matches, s_matches, new_matches) = gen_topo_ovs._color_assignment(pre_matches, cur_matches_fully, switch_nums)
        print len(d_matches), len(s_matches), len(new_matches)

        if True:

            for k in new_matches:
                print 'new_matches',k, len(new_matches[k])
                print sorted(new_matches[k])
            
            for k in s_matches:
                print 's_matches',k, len(s_matches[k])
                print sorted(s_matches[k])
            d_sum = 0
            for k in d_matches:
                d_sum += len(d_matches[k])
                print 'd_matches',k, len(d_matches[k])
                print sorted(d_matches[k])
            print d_sum
            